<?php
defined('FRAMEWORK_PATH') || exit;
class db_pdo_mysql implements db_interface {
	public $tablepre;		// 数据表前缀
	public $conf = array(); // 配置，可以支持主从

	public function __construct(&$conf) {
		$this->conf = &$conf;
		$this->tablepre = $conf['master']['tablepre'];
	}

	/**
	 * 创建 MySQL 连接
	 * @param string $var 数据库链接名 只能是 wlink rlink xlink
	 * @return resource
	 */
	public function __get($var) {
		// 主数据库 (写)
		if($var == 'wlink') {
			$cfg = $this->conf['master'];
			empty($cfg['engine']) && $cfg['engine'] = 'MyISAM';
			$this->wlink = $this->connect($cfg['host'], $cfg['port'], $cfg['user'], $cfg['password'], $cfg['name'], $cfg['charset'], $cfg['engine']);
			return $this->wlink;

			// 从数据库群 (读)
		}elseif($var == 'rlink') {
			if(!isset($this->conf['slaves']) || empty($this->conf['slaves'])) {
				$this->rlink = $this->wlink;
				return $this->rlink;
			}
			$n = rand(0, count($this->conf['slaves']) - 1);
			$cfg = $this->conf['slaves'][$n];
			empty($cfg['engine']) && $cfg['engine'] = 'MyISAM';
			$this->rlink = $this->connect($cfg['host'], $cfg['port'], $cfg['user'], $cfg['password'], $cfg['name'], $cfg['charset'], $cfg['engine']);
			return $this->rlink;

			// 单点分发数据库 (负责所有表的 maxid count 读写)
		}elseif($var == 'xlink') {
			if(empty($this->conf['arbiter'])) {
				$this->xlink = $this->wlink;
				return $this->xlink;
			}
			$cfg = $this->conf['arbiter'];
			empty($cfg['engine']) && $cfg['engine'] = 'MyISAM';
			$this->xlink = $this->connect($cfg['host'], $cfg['port'], $cfg['user'], $cfg['password'], $cfg['name'], $cfg['charset'], $cfg['engine']);
			return $this->xlink;
		}
	}

	/**
	 * 读取一条数据
	 * @param string $key	键名 (高性能需求，键名必须使用索引字段)
	 * @return array
	 */
	// string
	// 	in: 'user-uid-2'
	// 	out: array('uid'=>2, 'username'=>'two')
	public function get($key) {
		list($table, $keyarr, $keystr) = $this->key2arr($key);
		empty($link) && $link = $this->rlink;
		$sql = "SELECT * FROM {$this->tablepre}$table WHERE $keystr LIMIT 1";
		$result = $this->query($sql);
		if(is_object($result)){
			$r = $result->setFetchMode(PDO::FETCH_ASSOC);
			if($r){
                return $result->fetch();
            }else{
			    return array();
            }
		} else {
			$error = $link->errorInfo();
			throw new Exception("Errno: $error[0], Errstr: $error[2]");
		}
	}

	/**
	 * 读取多条数据
	 * @param array $keys	键名数组 (高性能需求，键名必须使用索引字段)
	 * @return array
	 */
	// array
	// 	in: array(
	// 		'article-cid-1-aid-1',
	// 		'article-cid-1-aid-2',
	// 	)
	// 	out: array(
	// 		'article-cid-1-aid-1'=>array('cid'=>1,'cid'=>1, 'title'=>'abc')
	// 		'article-cid-1-aid-2'=>array('cid'=>1,'cid'=>2, 'title'=>'bcd')
	// 	)
	public function multi_get($keys) {
		// 下面这种方式读取比遍历读取效率高
		$sql = '';
		$ret = array();
		foreach($keys as $k) {
			$ret[$k] = array();	// 按原来的顺序赋值，避免后面的 OR 条件取出时顺序混乱
			list($table, $keyarr, $keystr) = $this->key2arr($k);
			$sql .= "$keystr OR ";
		}
		$sql = substr($sql, 0, -4);
		if($sql) {
			$query = $this->query("SELECT * FROM {$this->tablepre}$table WHERE $sql");
			if(!is_object($query)){
                if( defined('DEBUG') && DEBUG ){
                    $error = $this->rlink->errorInfo();
                    throw new Exception("Errno: $error[0], Errstr: $error[2]");
                }
			    return array();
            }
			$r = $query->setFetchMode(PDO::FETCH_ASSOC);
			if( !$r ){
			    return array();
            }

			$row = $query->fetchAll();

			foreach($row as $data) {
				$keyname = $table;
				foreach($keyarr as $k=>$v) {
					$keyname .= "-$k-".$data[$k];
				}
				$ret[$keyname] = $data;
			}
		}
		return $ret;
	}

	/**
	 * 写入一条数据 (包含了 insert 和 update)
	 * @param string $key	键名
	 * @param array $data	数据
	 * @return bool
	 */
	public function set($key, $data) {
		if(!is_array($data)) return FALSE;

		list($table, $keyarr) = $this->key2arr($key);
		$data += $keyarr;
		$s = $this->arr2sql($data);

		$exists = $this->get($key);
		if(empty($exists)) {
			return $this->query("INSERT INTO {$this->tablepre}$table SET $s", $this->wlink);
		} else {
			return $this->update($key, $data);
		}
	}

	/**
	 * 更新一条数据 (相比 $this->set() 可以修改主键)
	 * @param string $key	键名
	 * @param array $data	数据
	 * @return bool
	 */
	public function update($key, $data) {
		list($table, $keyarr, $keystr) = $this->key2arr($key);
		$s = $this->arr2sql($data);
		return $this->query("UPDATE {$this->tablepre}$table SET $s WHERE $keystr LIMIT 1", $this->wlink);
	}

	/**
	 * 删除一条数据
	 * @param string $key	键名
	 * @return bool
	 */
	public function delete($key) {
		list($table, $keyarr, $keystr) = $this->key2arr($key);
		return $this->query("DELETE FROM {$this->tablepre}$table WHERE $keystr LIMIT 1", $this->wlink);
	}

	/**
	 * 读取/设置 表最大ID
	 * @param string $key	键名 只能是表名+一个字段 如：'user-uid'(uid为自增字段)
	 * @param boot/int $val	设置值 有三种情况 1.不填为读取(默认) 2.基础上增加 如：'+1' 3.设置指定值
	 * @return int
	 */
	// maxid('user-uid') 读取 user 表最大 uid
	// maxid('user-uid', '+1') 设置 maxid + 1, 用于占位，保证 key 不会重复
	// maxid('user-uid', 10000) 设置 maxid 为 10000
	public function maxid($key, $val = FALSE) {
		list($table, $col) = explode('-', $key);
		$maxid = $this->table_maxid($key);
		if($val === FALSE) {
			return $maxid;
		}elseif(is_string($val)) {
			$val = max(0, $maxid + intval($val));
		}
		$this->query("UPDATE {$this->tablepre}framework_maxid SET maxid='$val' WHERE name='$table' LIMIT 1", $this->xlink);
		return $val;
	}

	/**
	 * 读取表最大ID (如果不存在自动创建表和设置最大ID)
	 * @param string $key	键名 只能是表名+一个字段 如：'user-uid'(uid一般为主键)
	 * @return int
	 */
	public function table_maxid($key) {
		list($table, $maxid) = explode('-', $key);
		$arr = $this->fetch_first("SELECT MAX($maxid) AS num FROM {$this->tablepre}$table");
		return !empty($arr) ? intval($arr['num']) : $arr;
	}

	/**
	 * 读取/设置 表的总行数
	 * @param string $table	表名
	 * @param boot/int $val	设置值 有四种情况 1.不填为读取(默认) 2.基础上增加 如：'+1' 3.基础上减少 如：'-1' 4.设置指定值
	 * @return int
	 */
	public function count($table, $val = FALSE) {
		$count = $this->table_count($table);
		if($val === FALSE) {
			return $count;
		}elseif(is_string($val)) {
			if($val[0] == '+') {
				$val = $count + intval($val);
			}elseif($val[0] == '-') {
				$val = max(0, $count + intval($val));
			}
		}
		$this->query("UPDATE {$this->tablepre}framework_count SET count='$val' WHERE name='$table' LIMIT 1", $this->xlink);
		return $val;
	}

	/**
	 * 读取表的总行数 (如果不存在自动创建表和设置总行数)
	 * @param string $table	表名
	 * @return int
	 */
	public function table_count($table) {
		$count = 0;
		try {
			$arr = $this->fetch_first("SELECT COUNT(*) AS num FROM {$this->tablepre}$table", $this->xlink);
			$count = isset($arr['num']) ? intval($arr['num']) : 0;
		} catch (Exception $e) {
			throw new Exception('table_count 错误');
		}
		return $count;
	}

	/**
	 * 清空表
	 * @param string $table	表名 (表不存在会报错，但无关紧要)
	 * @return int
	 */
	public function truncate($table) {
		try {
			$this->query("TRUNCATE {$this->tablepre}$table");
			return TRUE;
		} catch(Exception $e) {
			return FALSE;
		}
	}

	/**
	 * 读取表字段
	 * @param string $table	表名，需要传递表前缀哦~
	 * @return array
	 */
	public function get_field($table){
		$ret = array();
		$cfg = $this->conf['master'];
		$sql = "SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE table_name = '".$this->tablepre.$table."' AND table_schema='".$cfg['name']."'";
		$query = $this->rlink->query($sql);
		if(!is_object($query)) {
            if( defined('DEBUG') && DEBUG ){
                $error = $this->rlink->errorInfo();
                throw new Exception("Errno: $error[0], Errstr: $error[2]");
            }
			return array();
		}
		$r = $query->setFetchMode(PDO::FETCH_ASSOC);
        if( !$r ){
            return array();
        }
		$row = $query->fetchAll();
		foreach($row as $data) {
			$ret[] = $data['COLUMN_NAME'];
		}
		return $ret;
	}

	/**
	 * 判断表是否存在
	 * @param string $table 表名
	 */
	public function exist_table($table){
		$db_name = $this->conf['master']['name'];
		$sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='".$db_name."' and table_name='".$this->tablepre.$table."'";
		$query = $this->rlink->query($sql);
        if(!is_object($query)) {
            if( defined('DEBUG') && DEBUG ){
                $error = $this->rlink->errorInfo();
                throw new Exception("Errno: $error[0], Errstr: $error[2]");
            }
			return false;
		}
		$r = $query->setFetchMode(PDO::FETCH_ASSOC);
        if( !$r ){
            return false;
        }
		$row = $query->fetchAll();
		if( $row ){
			return true;
		}else{
			return false;
		}
	}

	/**
	 * 根据条件读取数据 (返回数组)
	 * @param string $table	表名
	 * @param array $pri	主键
	 * @param array $where	条件
	 * @param array $order	排序
	 * @param int $start	开始位置
	 * @param int $limit	读取几条
	 * @return array
	 */
	// in:
	// 	find_fetch('user', 'uid', array('uid'=> 100), array('uid'=>1), 0, 10);
	// 	find_fetch('user', 'uid', array('uid'=> array('>'=>'100', '<'=>'200')), array('uid'=>1), 0, 10);
	// 	find_fetch('user', 'uid', array('username'=> array('LIKE'=>'abc'), array('uid'=>1), 0, 10);

	// out:
	// 	array(
	// 		'user-uid-1'=>array('uid'=>1, 'username'=>'zhangsan'),
	// 		'user-uid-2'=>array('uid'=>2, 'username'=>'lisi'),
	// 		'user-uid-3'=>array('uid'=>3, 'username'=>'wangwu'),
	// 	)
	public function find_fetch($table, $pri, $where = array(), $order = array(), $start = 0, $limit = 0) {
		$key_arr = $this->find_fetch_key($table, $pri, $where, $order, $start, $limit);
		if(empty($key_arr)) return array();
		return $this->multi_get($key_arr);
	}

	/**
	 * 根据条件返回 key 数组
	 * @param string $table	表名
	 * @param array $pri	主键
	 * @param array $where	条件
	 * @param array $order	排序
	 * @param int $start	开始位置
	 * @param int $limit	读取几条
	 * @return array
	 */
	// out:
	// 	array (
	// 		'user-uid-1',
	// 		'user-uid-2',
	// 		'user-uid-3',
	// 	)
	public function find_fetch_key($table, $pri, $where = array(), $order = array(), $start = 0, $limit = 0) {
		$pris = implode(',', $pri);
		$s = "SELECT $pris FROM {$this->tablepre}$table";
		$s .= $this->arr2where($where);
		if(!empty($order)) {
			$s .= ' ORDER BY ';
			$comma = '';
			foreach($order as $k=>$v) {
				$s .= $comma."$k ".($v == 1 ? ' ASC ' : ' DESC ');
				$comma = ',';
			}
		}
		$s .= ($limit ? " LIMIT $start,$limit" : '');

		$sql = $s;
		$ret = array();
		$query = $this->rlink->query($sql);
        if(!is_object($query)) {
            if( defined('DEBUG') && DEBUG ){
                $error = $this->rlink->errorInfo();
                throw new Exception("Errno: $error[0], Errstr: $error[2]");
            }
			return array();
		}
		$r = $query->setFetchMode(PDO::FETCH_ASSOC);
        if( !$r ){
            return array();
        }

		$row = $query->fetchAll();
		foreach($row as $data) {
			$keystr = '';
			foreach($pri as $k) {
				$keystr .= "-$k-".$data[$k];
			}
			$ret[] = $table.$keystr;
		}
		return $ret;
	}

	/**
	 * 根据条件批量更新数据
	 * @param string $table	表名
	 * @param array $where	条件
     * @param array $data 更新项
     * @param array $order 排序
     * @param int $limit	更新几条
	 * @param array $lowprority	是否开启不锁定表
	 * @return int	返回影响的记录行数
	 */
	public function find_update($table, $where, $data, $order = array(), $limit = 0, $lowprority = FALSE) {
		$where = $this->arr2where($where);
		$data = $this->arr2sql($data);
		$lpy = $lowprority ? 'LOW_PRIORITY' : '';

        $s = '';
        //排序
        if($order) {
            $s .= ' ORDER BY ';
            $comma = '';
            foreach($order as $k=>$v) {
                $s .= $comma."$k ".($v == 1 ? ' ASC ' : ' DESC ');
                $comma = ',';
            }
        }
        //条数
		if($limit){
            $s .= " LIMIT {$limit}";
        }

		return $this->exec("UPDATE $lpy {$this->tablepre}$table SET $data $where $s", $this->wlink);
	}

    /**
     * 更新一条数据的浏览量
     * @param string $key	键名
     * @param int $n		次数
     * @param string $field	浏览量字段
     * @return bool
     */
    public function update_views($key, $n = 1, $field = 'views'){
        list($table, $keyarr, $keystr) = $this->key2arr($key);

        $s = "{$field}={$field}+{$n}";
        return $this->query("UPDATE LOW_PRIORITY {$this->tablepre}$table SET $s WHERE $keystr LIMIT 1", $this->wlink);
    }

	/**
	 * 根据条件批量删除数据
	 * @param string $table	表名
	 * @param array $where	条件
     * @param array $order 排序
     * @param int $limit	删除几条
	 * @param array $lowprority	是否开启不锁定表
	 * @return int	返回影响的记录行数
	 */
	public function find_delete($table, $where, $order = array(), $limit = 0, $lowprority = FALSE) {
		$where = $this->arr2where($where);
		$lpy = $lowprority ? 'LOW_PRIORITY' : '';

        $s = '';
        //排序
        if($order) {
            $s .= ' ORDER BY ';
            $comma = '';
            foreach($order as $k=>$v) {
                $s .= $comma."$k ".($v == 1 ? ' ASC ' : ' DESC ');
                $comma = ',';
            }
        }
        //条数
        if($limit){
            $s .= " LIMIT {$limit}";
        }

		return $this->exec("DELETE $lpy FROM {$this->tablepre}$table $where $s", $this->wlink);
	}

	/**
	 * 准确获取最大ID
	 * @param string $key	键名
	 * @return int
	 */
	public function find_maxid($key) {
		list($table, $maxid) = explode('-', $key);
		$arr = $this->fetch_first("SELECT MAX($maxid) AS num FROM {$this->tablepre}$table");
		return isset($arr['num']) ? intval($arr['num']) : 0;
	}

	/**
	 * 准确获取总条数
	 * @param string $table	表名
	 * @param array $where	条件
	 * @return int
	 */
	public function find_count($table, $where = array()) {
		$where = $this->arr2where($where);
		$arr = $this->fetch_first("SELECT COUNT(*) AS num FROM {$this->tablepre}$table $where");
		return isset($arr['num']) ? intval($arr['num']) : 0;
	}

	/**
	 * 准确获取总条数
	 * @param string $table	表名
	 * @param array $where	条件
	 * @return int
	 */
	public function find_count_col($key,$where = array()) {
		$where = $this->arr2where($where);

		list($table, $filed) = explode('-', $key);
		$res = 0;
		$query = $this->fetch_all("SELECT IFNULL(sum($filed),0) AS money FROM {$this->tablepre}$table $where");
		if($query) {
			$res = $query[0]['money'];
		}
		return $res;
	}

	/**
	 * 创建索引
	 * @param string $table	表名
	 * @param array $index	键名数组	// array('uid'=>1, 'dateline'=>-1, 'unique'=>TRUE, 'dropDups'=>TRUE) 为了配合 mongodb 的索引才这样设计的
	 * @return boot
	 */
	public function index_create($table, $index) {
		$keys = implode(',', array_keys($index));
		$keyname = implode('_', array_keys($index));
		return $this->query("ALTER TABLE {$this->tablepre}$table ADD INDEX $keyname($keys)", $this->wlink);
	}

	/**
	 * 删除索引
	 * @param string $table	表名
	 * @param array $index	键名数组
	 * @return boot
	 */
	public function index_drop($table, $index) {
		$keys = implode(',', array_keys($index));
		$keyname = implode('_', array_keys($index));
		return $this->query("ALTER TABLE {$this->tablepre}$table DROP INDEX $keyname", $this->wlink);
	}

	// 创建表
	public function table_create($table, $cols, $engineer = '') {
		empty($engineer) && $engineer = 'MyISAM';
		$sql = "CREATE TABLE IF NOT EXISTS {$this->tablepre}$table (\n";
		$sep = '';
		foreach($cols as $col) {
			if(strpos($col[1], 'int') !== FALSE) {
				$sql .= "$sep$col[0] $col[1] NOT NULL DEFAULT '0'";
			} else {
				$sql .= "$sep$col[0] $col[1] NOT NULL DEFAULT ''";
			}
			$sep = ",\n";
		}
		$sql .= ") ENGINE=$engineer DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;";
		return $this->query($sql, $this->wlink);
	}

	// 删除表
	public function table_drop($table) {
		$sql = "DROP TABLE IF EXISTS {$this->tablepre}$table";
		return $this->query($sql, $this->wlink);
	}

	//删除数据库
    public function delete_db(){
	    $sql = "DROP DATABASE IF EXISTS ".$this->conf['master']['name'];
        return $this->query($sql, $this->wlink);
    }

	//表 增加字段
	public function table_column_add($table, $colname, $colattr) {
		$default = strpos($colattr, 'int') !== FALSE ? "'0'" : "''";
		$sql = "ALTER TABLE {$this->tablepre}$table ADD COLUMN $colname $colattr NOT NULL DEFAULT $default;";
		try {$this->query($sql, $this->wlink);} catch (Exception $e) {};
		return TRUE;
	}

	//表 删除字段
	public function table_column_drop($table, $colname) {
		$sql = "ALTER TABLE {$this->tablepre}$table DROP COLUMN $colname;";
		try {$this->query($sql, $this->wlink);} catch (Exception $e) {};
		return TRUE;
	}

	// +------------------------------------------------------------------------------
	// | 以下是公共方法，但不推荐外部使用
	// +------------------------------------------------------------------------------
	/**
	 * 连接 MySQL 服务器
	 * @param string $host		主机
	 * @param string $user		用户名
	 * @param string $pass		密码
	 * @param string $name		数据库名称
	 * @param string $charset	字符集
	 * @param string $engine	数据库引擎
	 * @return resource
	 */
	public function connect($host, $port, $user, $pass, $name, $charset = 'utf8', $engine = '') {
		if(strpos($host, ':') !== FALSE) {
			list($host, $port) = explode(':', $host);
		} else {
			$port = !empty($port) ? $port : 3306;
		}
		try{
			$attr = array(
				PDO::ATTR_TIMEOUT => 5,
			);
			$link = new PDO("mysql:host=$host;port=$port;dbname=$name;charset=$charset", $user, $pass, $attr);
		} catch (Exception $e) {
			throw new Exception('连接数据库服务器失败:'.mb_convert_encoding($e->getMessage(),'UTF-8','GBK'));
		}
		if($charset){
			$link->query('SET NAMES '.$charset.', sql_mode=""');
		}else{
			$link->query('SET sql_mode=""');
		}
		return $link;
	}

	/**
	 * 发送一条 MySQL 查询
	 * @param string $sql		SQL 语句
	 * @param string $link		打开的连接
	 * @param boot $isthrow		错误时是否抛
	 * @return resource
	 */
	public function query($sql, $link = NULL, $isthrow = TRUE) {
		if(!$this->rlink) return FALSE;
		$link = $this->link = $this->rlink;
		try {
			if(defined('DEBUG') && DEBUG && isset($_ENV['_sqls']) && count($_ENV['_sqls']) < 1000) {
				$start = microtime(1);
				$query = $link->query($sql);
				$runtime = number_format(microtime(1) - $start, 4);
				$_ENV['_sqls'][] = ' <font color="red">[time:'.$runtime.'s]</font> '.htmlspecialchars(stripslashes($sql));
			}else{
				$query = $link->query($sql);
			}
		} catch (Exception $e) {
			$s = $link->errorInfo();
			$s = str_replace($this->tablepre, '***', $s); // 防止泄露敏感信息
			throw new Exception('Pdo_MySQL Query Error:'.$sql.' '.(isset($s[2]) ? "Errstr: $s[2]" : ''));
		}
		$_ENV['_sqlnum']++;
		return $query;
	}

	public function exec($sql) {
		if(!$this->wlink ) return FALSE;
		$link = $this->link = $this->wlink;
		$n = 0;
		try {
            if(defined('DEBUG') && DEBUG && isset($_ENV['_sqls']) && count($_ENV['_sqls']) < 1000) {
                $start = microtime(1);
                $n = $link->exec($sql); // 返回受到影响的行，插入的 id ?
                $runtime = number_format(microtime(1) - $start, 4);
                $_ENV['_sqls'][] = ' <font color="red">[time:'.$runtime.'s]</font> '.htmlspecialchars(stripslashes($sql));
            }else{
                $n = $link->exec($sql); // 返回受到影响的行，插入的 id ?
            }
		} catch (Exception $e){
			$s = $link->errorInfo();
			$s = str_replace($this->tablepre, '***', $s); // 防止泄露敏感信息
			throw new Exception('Pdo_MySQL Query Error:'.$sql.' '.(isset($s[2]) ? "Errstr: $s[2]" : ''));
			return FALSE;
		}
        $_ENV['_sqlnum']++;

		if($n !== FALSE) {
			$pre = strtoupper(substr(trim($sql), 0, 7));
			if($pre == 'INSERT ' || $pre == 'REPLACE') {
				return $this->last_insert_id();
			}
		} else {
			$s = $link->errorInfo();
			$s = str_replace($this->tablepre, '***', $s); // 防止泄露敏感信息
			throw new Exception('Pdo_MySQL Query Error:'.$sql.' '.(isset($s[2]) ? "Errstr: $s[2]" : ''));
		}
		return $n;
	}

	public function last_insert_id() {
		return $this->wlink->lastinsertid();
	}

	/**
	 * 获取第一条数据
	 * @param string $sql		SQL 语句
	 * @param string $link		打开的连接
	 * @return array
	 */
	public function fetch_first($sql, $link = NULL) {
        empty($link) && $link = $this->rlink;
		$result = $this->query($sql, $link);
		if(is_object($result)) {
			$r = $result->setFetchMode(PDO::FETCH_ASSOC);
            if( !$r ){
                return array();
            }
			return $result->fetch();
		} else {
			$error = $link->errorInfo();
			throw new Exception("Errno: $error[0], Errstr: $error[2]");
		}
	}

	/**
	 * 获取多条数据 (特殊情况会用到)
	 * @param string $sql		SQL 语句
	 * @param string $link		打开的连接
	 * @return array
	 */
	public function fetch_all($sql, $link = NULL) {
        empty($link) && $link = $this->rlink;
		$result = $this->query($sql, $link);
		if(is_object($result)) {
			$r = $result->setFetchMode(PDO::FETCH_ASSOC);
            if( !$r ){
                return array();
            }
			$datalist = $result->fetchAll();
			return $datalist;
		} else {
			$error = $link->errorInfo();
			throw new Exception("Errno: $error[0], Errstr: $error[2]");
		}
	}

	/**
	 * 获取 mysql 版本
	 * @return string
	 */
	public function version() {
		return 'pdo_mysql';
	}

	/**
	 * 关闭读写数据库连接
	 */
	public function __destruct() {
		if(is_resource($this->wlink)) {
			$this->wlink = NULL;
		}
		if(is_resource($this->rlink) && is_resource($this->wlink) && $this->rlink != $this->wlink) {
			$this->rlink = NULL;
		}
	}

	/**
	 * 将数组转换为 where 语句
	 * @param array $arr 数组
	 * @return string
	 * in: array('id'=> array('>'=>'10', '<'=>'200'))
	 * out: WHERE id>'10' AND id<'200'
	 * 支持: '>=', '<=', '>', '<', 'LIKE', 'IN' (尽量少用，能不用则不用。'LIKE' 会导致全表扫描，大数据时不要使用)
	 * 注意1: 为考虑多种数据库兼容和性能问题，其他表达式不要使用，如：!= 会导致全表扫描
	 * 注意2: 高性能准则要让SQL走索引，保证查询至少达到range级别
	 */
	public function arr2where($arr) {
		$s = '';
		if(!empty($arr)) {
			foreach($arr as $key=>$val) {
				if(is_array($val)) {
					foreach($val as $k=>$v) {
						if(is_array($v)) {
							if($k === 'IN' && $v) {
								$s .= ' (';
								foreach($v as $i) {
                                    $i = is_null($i) ? '' : addslashes($i);
									$s .= "$key='$i' OR "; // 走索引时，OR 比 IN 快
								}
								$s = substr($s, 0, -4).') AND ';
							}
						}else{
                            $v = is_null($v) ? '' : addslashes($v);
							if($k === 'LIKE') {
								$s .= "$key LIKE '%$v%' AND ";
							}elseif($k === 'FIND_IN_SET') {
								$s .= "FIND_IN_SET ('$v', $key) AND ";
							}else{
								$s .= "$key$k'$v' AND ";
							}
						}
					}
				}else{
                    $val = is_null($val) ? '' : addslashes($val);
					$s .= "$key='$val' AND ";
				}
			}
			$s && $s = ' WHERE '.substr($s, 0, -5);
		}
		return $s;
	}

	/**
	 * 将数组转换为SQL语句
	 * @param array $arr 数组
	 * @return string
	 * in: array('cid'=>1, 'aid'=>2)
	 * out: cid='1',aid='2'
	 */
	private function arr2sql($arr) {
		$s = '';
		foreach($arr as $k=>$v) {
            $v = is_null($v) ? '' : addslashes($v);
			$s .= "$k='$v',";
		}
		return rtrim($s, ',');
	}

	/**
	 * 将键名转换为数组
	 * @param string $key	键名
	 * @return array
	 * in: article-cid-1-aid-2
	 * out: array('article', array('cid'=>1, 'aid'=>2), 'cid=1 AND aid=2')
	 */
	private function key2arr($key) {
		$arr = explode('-', $key);

		if(empty($arr[0])) {
			throw new Exception('table name is empty.');
		}

		$table = $arr[0];
		if($table == 'only_alias'){	//add by dadadezhou， 别名表的alias支持 横线
			$keyval = substr($key, 17);	//only_alias-alias-
			$keystr = "alias='".$keyval."'";
			$keyarr['alias'] = $keyval;
			return array($table, $keyarr, $keystr);
		}
		$keyarr = array();
		$keystr = '';
		$len = count($arr);
		for($i = 1; $i < $len; $i = $i + 2) {
			if(isset($arr[$i + 1])) {
				$v = $arr[$i + 1];
				$keyarr[$arr[$i]] = is_int($v) ? intval($v) : $v;	// 因为 mongodb 区分数字和字符串

                $v = is_null($v) ? '' : addslashes($v);
				$keystr .= ($keystr ? ' AND ' : '').$arr[$i]."='".$v."'";
			} else {
				$keyarr[$arr[$i]] = NULL;
			}
		}

		if(empty($keystr)) {
			throw new Exception('keystr name is empty.');
		}
		return array($table, $keyarr, $keystr);
	}
}
